package org.simpleframework.util;

import org.simpleframework.singleton.EnumSingleton;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
//根据class文件的绝对值路径，生成class对象，并放入classSet中
//1.从class文件的绝对值路径里提取出包含了packageName的类名
//D:\project\workspace\simple-framwork\target\classes\com\zggao\annotation\CourseAnnotation.class
//com.zggao.annotation.CourseAnnotation
//2.通过反射机制获取对应的Class对象并加入到classSet里
//3.
public class ClassUtil {
    public static final String FILE_PROTOCOL = "file";
    public static Set<Class<?>> extractPackageClass(String userDefinePckName){
        Set<Class<?>> classSet = null;
        // 1.获取类的加载器
        ClassLoader classLoader = getClassLoader();
        // 2.通过类加载器获取到加载的资源的实际路径
        //file:/D:/project/workspace/simple-framwork/target/classes/com/zggao/entity
        URL url = classLoader.getResource(userDefinePckName.replace(".","/"));
        // 获取资源协议，判断是不是本地协议（file）
        if(url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
            classSet = new HashSet<>();
            // 获取packageName对应的实际路径
            File rootPackageName = new File(url.getPath());
            // 提取class文件，生成对象实例,存入到classSet中
            extractClassFile(classSet,rootPackageName,userDefinePckName);
        }
        return classSet;
    }

    /**
     * 递归获取目标 package目录下的所有class文件（包括子package目录下的文件）
     * @param emptyClassSet 收集目标类的结果
     * @param fileSource 文件或目录
     * @param packageName 包名
     */
    private static void extractClassFile(Set<Class<?>> emptyClassSet, File fileSource, String packageName) {
        // 递归终止条件
        if(!fileSource.isDirectory()){
            // 不是目录就终止
            return;
        }
        // listFiles() 过滤出当前目录下的子文件夹及文件
        // FileFilter 根据规则过滤出需要的文件或文件夹
        File[] files = fileSource.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                // 如果是目录，则刷选出来
                if(file.isDirectory()){
                    return true;
                }else {
                    // 获取文件的绝对路径
                    String absoluteFilePath = file.getAbsolutePath();
                    if(absoluteFilePath.endsWith(".class")){
                        // 若是class文件，则加载类并生成类的实例，存储到set中
                        addToClassSet(absoluteFilePath);
                    }
                }
                // 对于其他资源直接排除掉，不需要此类资源
                return false;
            }
            //absoluteFilePath =
                //D:\project\workspace\simple-framwork\target\classes\com\zggao\entity\bo\HeadLine.class
            private void addToClassSet(String absoluteFilePath) {
                absoluteFilePath = absoluteFilePath.replace(File.separator,".");
                //D:.project.workspace.simple-framwork.target.classes.com.zggao.entity.bo.HeadLine.class
                String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
                //com.zggao.entity.bo.HeadLine.class
                className = className.substring(0,className.lastIndexOf("."));
                //com.zggao.entity.bo.HeadLine
                Class targetClass = loadClass(className);
                //class com.zggao.entity.bo.HeadLine
                emptyClassSet.add(targetClass);
            }
        });
        if(files != null){
            for(File f:files){
                extractClassFile(emptyClassSet,f,packageName);
            }
        }
    }

    public static Class<?> loadClass(String className){
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    // 获取当前线程的类加载器，每一个线程都有一个类加载器与之对应（除非该线程是被本地代码创建的）
    // 线程创建好后，会设置它的类加载Thread.setContextClassLoader()
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

    public static <T> T newInstance(Class clazz){
        try {
            return (T)clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    //给target类的field成员变量设置value值
    /**
     *
     * @param field 成员变量
     * @param target 成员变量所属的类实例
     * @param value 成员变量的值
     * @param accessible 是否允许设置私有属性
     */
    public static void setField(Field field, Object target, Object value, boolean accessible){
        field.setAccessible(accessible);
        try {
            // 给目标的成员变量设值
            field.set(target,value);
        } catch (IllegalAccessException e) {
            //记录日志
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //extractPackageClass("com.zggao.entity");
        System.out.println(EnumSingleton.getInstance());
        Class clazz = EnumSingleton.class;
        Constructor c = clazz.getDeclaredConstructor();
        c.setAccessible(true);
        EnumSingleton b = (EnumSingleton)c.newInstance();
        System.out.println();
    }
}
